#include "proto/rpcserver.pb.h"
#include "../server/logging/Logging.h"
#include "../server/net/EventLoop.h"
#include "protorpc/RpcServer.h"
#include <unistd.h>
#include <lua.hpp>
#include <boost/dll.hpp>
#include <boost/filesystem.hpp>
// #include <boost/algorithm/string/classification.hpp>
// #include <boost/algorithm/string/split.hpp>
#include <map>
using namespace yhaida;

typedef int(*FuncPoint)(lua_State *L);
lua_State *lua;
std::map<std::string, std::shared_ptr<boost::dll::shared_library>> hash;

static int padd(lua_State *L)
{
    int n = lua_gettop(L);
    int sum = 0;
    for (int i = 1; i <= n; ++i)
        sum += lua_tonumber(L, i);
    lua_pushnumber(L, sum);
    return 1;
}
static int psub(lua_State *L)
{
    int a = lua_tonumber(L, -2);
    int b = lua_tonumber(L, -1);
    int result = 0;
    result = a - b;
    lua_pushnumber(L, result);
    return 1;
}

static int pmul(lua_State *L)
{
    int a = lua_tonumber(L, -2);
    int b = lua_tonumber(L, -1);
    int result = 0;
    result = a * b;
    lua_pushnumber(L, result);
    return 1;
}

static int pdiv(lua_State *L)
{
    int a = lua_tonumber(L, -2);
    int b = lua_tonumber(L, -1);
    int result = 0;
    result = a / b;
    lua_pushnumber(L, result);
    return 1;
}


void fileFunction(lua_State* lua)
{
    boost::filesystem::path path("../library");
    boost::filesystem::directory_iterator itr(path);
    while(itr != boost::filesystem::directory_iterator()) {
        // 判断该目录是文件夹，跳过
        if (boost::filesystem::is_directory(itr->path())) {
            itr++;
            continue;
        }
        std::string filePath = itr->path().string();
        // std::cout << filePath << std::endl;

        std::string stem = itr->path().stem().string();        // 文件名
        std::string libName = std::move(stem.substr(3));

        std::shared_ptr<boost::dll::shared_library> alib(new boost::dll::shared_library);;
        hash[stem]=alib;

        alib->load(filePath);
        auto f = alib->get_alias<int(lua_State *L)>(libName);
        lua_pushstring(lua, libName.c_str());
        lua_pushcfunction(lua, f);
        lua_settable(lua, -3);

        ++itr;
    }
}

void scriptingInit()
{
    lua = luaL_newstate();
    luaopen_base(lua);
    lua_newtable(lua);
    //注册
    lua_pushstring(lua, "add");
    lua_pushcfunction(lua, padd);
    lua_settable(lua, -3);
    lua_pushstring(lua, "sub");
    lua_pushcfunction(lua, psub);
    lua_settable(lua, -3);
    lua_pushstring(lua, "mul");
    lua_pushcfunction(lua, pmul);
    lua_settable(lua, -3);
    lua_pushstring(lua, "div");
    lua_pushcfunction(lua, pdiv);
    lua_settable(lua, -3);

    //遍历文件夹获取动态库
    fileFunction(lua);

    lua_setglobal(lua, "rpc");
}
class EchoServiceImpl : public rpc::EchoService
{
public:
    void Echo(::google::protobuf::RpcController *,
              const ::rpc::EchoRequest *requst,
              ::rpc::EchoResponse *response,
              ::google::protobuf::Closure *done) override
    {
        response->set_payload(requst->payload());
        done->Run();
    }
};
class RpcServiceImpl : public rpc::RpcService
{
public:
    void listRpc(::google::protobuf::RpcController *,
                 const ::rpc::ListRpcRequest *,
                 ::rpc::ListRpcResponse *response,
                 ::google::protobuf::Closure *done) override
    {
        // response->set_payload(requst->payload());
        // 返回lua中的函数
        lua_getglobal(lua, "rpc");
        int index = lua_gettop(lua);
        lua_pushnil(lua);
        // 现在的栈：-1 => nil; index => table
        // std::string* name;
        while (lua_next(lua, index))
        {
            lua_pushvalue(lua, -2);
            // std::string key = lua_tostring(lua, -1);
            // std::string value = lua_tostring(lua, -2);
            // name = response->add_function_name();
            // name->assign(lua_tostring(lua, -1));
            response->add_function_name(lua_tostring(lua, -1));
            // printf("%s => %s\n", key, value);
            lua_pop(lua, 2);
        }
        lua_pop(lua, -1);
        response->set_error(rpc::ErrorCode::NO_ERROR);
        done->Run();
    }
    void getService(::google::protobuf::RpcController *,
                    const ::rpc::GetServiceRequest *requst,
                    ::rpc::GetServiceResponse *response,
                    ::google::protobuf::Closure *done) override
    {
        // response->set_payload(requst->payload());
        // 根据requst运行函数
        std::string rpc_function = requst->function();
        luaL_loadbuffer(lua, rpc_function.c_str(), rpc_function.size(), "@client_func_def");
        lua_pcall(lua, 0, 0, 0);

        lua_getglobal(lua, "__rpc_function");
        lua_pcall(lua, 0, 1, 0);

        int result = lua_tonumber(lua, -1);
        lua_pop(lua, -1);
        response->set_error(rpc::ErrorCode::NO_ERROR);
        response->set_result(std::to_string(result));
        done->Run();
    }
};

int main(int argc, char *argv[])
{
    int nThreads = argc > 1 ? atoi(argv[1]) : 1;
    LOG_INFO << "pid = " << getpid() << " threads = " << nThreads;
    scriptingInit();
    EventLoop loop;
    int port = argc > 2 ? atoi(argv[2]) : 8000;
    InetAddress listenAddr(static_cast<uint16_t>(port));
    EchoServiceImpl impl;
    RpcServiceImpl impl2;
    rpc::RpcServer server(&loop, listenAddr);
    server.setThreadNum(nThreads);
    server.registerService(&impl);
    server.registerService(&impl2);
    server.start();
    loop.loop();
    lua_close(lua);
}
